﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using PowerTriggersWcf;
using System.Xml;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceProcess;
using System.Diagnostics;
using System.ComponentModel;

namespace PowerTriggersWcf
{
    [DataContract(Namespace = "http://PWTWcf")]
    internal class TaskStopProcess : Task
    {
        private string processName;

        #region constructors
        private TaskStopProcess()
            : base(TaskTypes.StopProcess, true)
        {
        }

        internal TaskStopProcess(bool setDefaultProperties)
            : base(TaskTypes.StopProcess, setDefaultProperties)
        {
        }

        internal TaskStopProcess(PWTProcess process)
            : this()
        {
            this.Description = process.Description;
            this.Sequence = 0;
            this.Status = string.Empty;
            this.ID = process.ExePath;
            this.Category = string.Empty;
            this.ProcessName = process.ProcessName;
        }

        internal TaskStopProcess(Process process)
            : this()
        {
            this.Description = ProcessLauncher.GetProcessDescription(process);
            this.Sequence = 0;
            this.Status = string.Empty;
            this.ID = ProcessLauncher.GetProcessExePath(process);
            this.Category = string.Empty;
        }
        #endregion

        internal string ProcessName
        {
            get
            {
                if (this.processName == null)
                {
                    this.processName = System.IO.Path.GetFileName(this.ID);
                }

                return this.processName;
            }
            set { this.processName = value; }
        }

        internal override bool PerformAction(HistoryHeader historyHeader)
        {
            bool success = true;

            try
            {
                TaskPropertyChoice choice = this.GetSelectedChoice();
                TaskPropertyChoice action = this.GetSelectedAction();

                Process[] processes = GetProcessesByName(this.ProcessName, choice);
                int index = 0;
                int count = processes.Length;
                foreach (var process in processes)
                {
                    index++;
                    string description = this.Description;
                    if (count > 1)
                    {
                        description += " [" + index.ToString() + " of " + count.ToString() + "]";
                    }
                    try
                    {
                        if (process.HasExited)
                        {
                            index--;
                            count--;
                        }
                        else
                        {
                            if (process.MainWindowHandle != IntPtr.Zero)
                            {
                                process.CloseMainWindow();
                                if (action.ChoiceType == TaskPropertyChoiceTypes.Pause)
                                {
                                    int? milliseconds = (int?)action.AdditionalData;
                                    if (milliseconds.HasValue)
                                    {
                                        System.Threading.Thread.Sleep(milliseconds.Value);
                                    }
                                }
                                else
                                {
                                    if (!process.HasExited)
                                    {
                                        Service.SavePendingHistory();
                                        if (action.ChoiceType == TaskPropertyChoiceTypes.WaitForExit)
                                        {
                                            process.WaitForExit(60000);
                                        }
                                        else
                                        {
                                            process.WaitForExit(2000);
                                        }
                                    }
                                }
                            }
                            if (!process.HasExited)
                            {
                                process.Kill();
                            }
                            Service.AddHistory(historyHeader, HistorySeverityLevels.Success, string.Format("Process '{0}' successfully stopped", description), false);
                        }
                        process.Dispose();
                    }
                    catch (System.Exception ex)
                    {
                        string msg = string.Format("Error when attempting to stop process '{0}'. Error={1}", description, ex.Message.Trim());
                        Service.AddHistory(historyHeader, HistorySeverityLevels.Error, msg, false);
                        success = false;
                    }
                }
                if (index == 0)
                {
                    Service.AddHistory(historyHeader, HistorySeverityLevels.Informational, string.Format("No running instances of process '{0}' found", this.Description), false);
                }
            }
            catch (System.Exception ex)
            {
                string msg = string.Format("Error when attempting to stop process '{0}'. Error={1}", this.Description, ex.Message.Trim());
                Service.AddHistory(historyHeader, HistorySeverityLevels.Error, msg, false);
                success = false;
            }

            return success;
        }

        private Process[] GetProcessesByName(string processName, TaskPropertyChoice choice)
        {
            IDictionary<int, string> validUsers;

            switch (choice.ChoiceType)
            {
                case TaskPropertyChoiceTypes.AllLoggedOnUsernames:
                    validUsers = ProcessLauncher.GetLoggedOnSessions();
                    break;
                case TaskPropertyChoiceTypes.SpecifiedUsername:
                    string username = ((string[])choice.AdditionalData)[0].ToUpper();
                    validUsers = ProcessLauncher.GetLoggedOnSessions()
                                                .Where(kvp => kvp.Value.ToUpper() == username)
                                                .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
                    break;
                default: // Any
                    validUsers = null;
                    break;
            }

            IList<PWTProcess> allProcesses;
            if (validUsers == null || validUsers.Count == 0)
            {
                allProcesses = PWTProcess.GetProcesses(processName);
            }
            else
            {
                allProcesses = PWTProcess.GetProcesses(processName, validUsers.Keys.ToList());
            }

            List<Process> matchingProcesses = new List<Process>();
            foreach (var process in allProcesses)
            {
                try
                {
                    Process proc = Process.GetProcessById(process.ProcessId);
                    if (!proc.HasExited)
                    {
                        matchingProcesses.Add(proc);
                    }
                }
                catch { }

                process.Dispose();
            }

            return matchingProcesses.ToArray();
        }
    }
}
